/*******************************************************************************
 * FILE NAME          : tp_linked_list_handler.c
 *
 * DESCRIPTION        : This file provides the functions handle for Linked-list
 *                      .This file contains following function definitions.
 *                      tp_init_thread_ll_func()
 *                      tp_list_all_thread_into_ll_func()
 *                      tp_get_tail_from_thread_ll_func()
 *                      tp_add_item_to_ll_func()
 *                      tp_rm_thread_from_ll_func()
 *                      tp_find_items_into_ll_func()
 *						tp_rm_empty_item_from_ll_func()
 *						tp_add_empty_item_to_ll_func()
 *
 * DESIGN REFERENCE   :
 *
 * STARTING VERSION:
 *
 *
 * REVISION HISTORY   :
 * Date            Author                   REASON
 * 06.08.2013    Thuan Nguyen           First draft and write handles for linked-list
 * ****************************************************************************/

/* Library includes */


#include"../h/tp_linked_list_def.h"
#include"../../common/h/syslog_api_platform.h"

threads_transport_t *g_threads_list = SYSLOG_NOK;
pthread_mutex_t g_thread_list_mutex;

/***********************************************************************
 * FUNCTION NAME:
 *  initial_threads_ll_func 
 * DESCRIPTION:
 *	Allocate first node for linked list.
 * INPUT:
 *
 * OUTPUT:
 *
 * RETURN:
 *
 * GLOBAL DATA REFERRED:
 *
 * CALLED ROUTINES:
 *
 * NOTES:
 *
 ************************************************************************/
U8 initial_threads_ll_func(thread_info_t thread_info)
{
	
	U8 val_result = SYSLOG_FALSE;
	pthread_mutex_lock(&g_thread_list_mutex);
	if(SYSLOG_NOK == g_threads_list)
	{
		//Allocate For Header.
		g_threads_list = T_OS_BLOCK_ALLOC(TASK_ID_TRANSPORT,sizeof(threads_transport_t));

		g_threads_list->p_next_threads = SYSLOG_NOK;
		g_threads_list->p_prev_threads = SYSLOG_NOK;
		g_threads_list->data_thread = thread_info;
		val_result = SYSLOG_TRUE;

	}
	else
	{
		SYSLOG_TRANSPORT_ERROR_LOGGING("g_thread_list has already initilized.");
	}
	pthread_mutex_unlock(&g_thread_list_mutex);
	return val_result;
}
/***********************************************************************
 * FUNCTION NAME: 
 *  tp_list_all_thread_into_ll_func
 * DESCRIPTION:
 *	This function will print all item into linked list to logs.
 * INPUT: 
 *	request_msg_t *req_msg
 * OUTPUT:
 *	
 * RETURN:
 *	val_result
 * GLOBAL DATA REFERRED:
 *
 * CALLED ROUTINES:
 *
 * NOTES:
 *
 ************************************************************************/
void tp_list_all_thread_into_ll_func(void)
{
	threads_transport_t *temp_thread;
	temp_thread = g_threads_list;

	while(SYSLOG_NULL != temp_thread)
	{
		switch(temp_thread->data_thread.thread_type)
		{
		case MASTER_THREAD_TYPE:
			SYSLOG_TRANSPORT_INFO_LOGGING("Thread Type: MASTER_THREAD_TYPE");
			break;
		case CONNECTION_THREAD_TYPE:
			SYSLOG_TRANSPORT_INFO_LOGGING("Thread Type: CONNECTION_THREAD_TYPE");
			break;
		case LISTEN_THREAD_TYPE:
			SYSLOG_TRANSPORT_INFO_LOGGING("Thread Type: LISTEN_THREAD_TYPE");
			break;
		case SENDER_THREAD_TYPE:
			SYSLOG_TRANSPORT_INFO_LOGGING("Thread Type: SENDER_THREAD_TYPE");
			break;
		default:
			SYSLOG_TRANSPORT_INFO_LOGGING("Thread Type: UNKNOW");
			break;
		}
		SYSLOG_TRANSPORT_INFO_LOGGING("Thread Key:%d ",temp_thread->data_thread.thread_key);
		temp_thread = temp_thread->p_next_threads;
	}
}
/***********************************************************************
 * FUNCTION NAME: 
 *  tp_get_tail_from_thread_ll_func
 * DESCRIPTION: 
 *	Get  end_node of linked-list.
 * INPUT: 
 *	request_msg_t *req_msg
 * OUTPUT:
 *	
 * RETURN:
 *	val_result
 * GLOBAL DATA REFERRED:
 *
 * CALLED ROUTINES:
 *
 * NOTES:
 *
 ************************************************************************/
void* tp_get_tail_from_thread_ll_func(threads_transport_t *p_thread_list){
	threads_transport_t *temp_thread = SYSLOG_NULL;
	temp_thread = p_thread_list;


	while(SYSLOG_NULL != temp_thread)
	{
		if(SYSLOG_NULL == temp_thread->p_next_threads){
			return temp_thread;
		}
		temp_thread = temp_thread->p_next_threads;
	}
	return temp_thread;
}

/***********************************************************************
 * FUNCTION NAME: 
 *  tp_add_item_to_ll_func
 *
 * DESCRIPTION: 
 *	Add new thread_connection to end of linked list.
 *
 * INPUT: 
 *	threads_transport_t *p_thread_list
 *	thread_info_t *thread_info
 *
 * OUTPUT:
 *	
 * RETURN:
 *	val_result
 * GLOBAL DATA REFERRED:
 *
 * CALLED ROUTINES:
 *
 * NOTES:
 *
 ************************************************************************/
U8 tp_add_item_to_ll_func(threads_transport_t *p_thread_list,thread_info_t *thread_info){
	int val_return = SYSLOG_FALSE;
	

	/*Validate thread_info*/
	if(SYSLOG_NOK == thread_info){
		SYSLOG_TRANSPORT_ERROR_LOGGING("thread_info invalid");
		return val_return;
	}

	/*Add node to linked-list have two case
	 * Case 1:
	 * 	g_threads_list is NULL ( There aren't any node into linked-list).
	 * Case 2:
	 *  There are more than 1 node into linked-list.
	 * */

	/*lock g_thread_list*/
	pthread_mutex_lock(&g_thread_list_mutex);

	/*Case 1*/
	if(SYSLOG_NULL == p_thread_list)
	{
		g_threads_list = T_OS_BLOCK_ALLOC(TASK_ID_TRANSPORT,sizeof(threads_transport_t));
		g_threads_list->p_next_threads = SYSLOG_NULL;
		g_threads_list->data_thread.thread_key = thread_info->thread_key;
		g_threads_list->data_thread.thread_type = thread_info->thread_type;
		g_threads_list->data_thread.thread_func = thread_info->thread_func;
		g_threads_list->data_thread.client_addr = thread_info->client_addr;
		g_threads_list->data_thread.socket_id = thread_info->socket_id;
		g_threads_list->p_prev_threads = SYSLOG_NULL;
		val_return = SYSLOG_TRUE;
	}
	else
	{
		/*Case 2*/
		threads_transport_t *temp_thread;
		/*Get Node tail of linked link threads*/
		temp_thread = tp_get_tail_from_thread_ll_func(p_thread_list);
		if(SYSLOG_NULL != temp_thread)
		{
			/*Allocation for position which will be add new node*/
			temp_thread->p_next_threads = T_OS_BLOCK_ALLOC(TASK_ID_TRANSPORT,sizeof(threads_transport_t));


			if(SYSLOG_NULL != temp_thread->p_next_threads){
				temp_thread->p_prev_threads = temp_thread;
				temp_thread = temp_thread->p_next_threads;
				temp_thread->p_next_threads = SYSLOG_NULL;
				temp_thread->data_thread.thread_type = thread_info->thread_type;
				temp_thread->data_thread.thread_key = thread_info->thread_key;
				temp_thread->data_thread.thread_func = thread_info->thread_func;
				val_return = SYSLOG_TRUE;
			}
		}
		else{
			SYSLOG_TRANSPORT_ERROR_LOGGING("Can't get tail thread from Linked-list");
			val_return = SYSLOG_FALSE;
		}

	}
	pthread_mutex_unlock(&g_thread_list_mutex);
	return val_return;
}
/***********************************************************************
 * FUNCTION NAME: 
 *  tp_add_empty_item_to_ll_func
 *
 * DESCRIPTION:
 *	Add a item to end of linked list.
 *	This item is empty and State is INACTIVE
 * INPUT:
 *	threads_transport_t *p_thread_list
 *	thread_info_t *thread_info
 *
 * OUTPUT:
 *
 * RETURN:
 *	val_result
 * GLOBAL DATA REFERRED:
 *
 * CALLED ROUTINES:
 *
 * NOTES:
 *
 ************************************************************************/
void* tp_add_empty_item_to_ll_func(threads_transport_t *p_thread_list){
	threads_transport_t *temp_thread;
	threads_transport_t *temp_new_thread = SYSLOG_NULL;
	/*Add node to linked-list have two case
	 * Case 1:
	 * 	g_threads_list is NULL ( There aren't any node into linked-list).
	 * Case 2:
	 *  There are more than 1 node into linked-list.
	 * */

	/*lock g_thread_list*/
	pthread_mutex_lock(&g_thread_list_mutex);

	/*Case 1*/
	if(SYSLOG_NULL == p_thread_list)
	{
		g_threads_list = T_OS_BLOCK_ALLOC(TASK_ID_TRANSPORT,sizeof(threads_transport_t));
		g_threads_list->p_next_threads = SYSLOG_NULL;
		g_threads_list->p_prev_threads = SYSLOG_NULL;
		temp_new_thread = g_threads_list;
	}
	else
	{
		/*Case 2*/
		/*Get Node tail of linked link threads*/
		temp_thread = tp_get_tail_from_thread_ll_func(p_thread_list);
		if(SYSLOG_NULL != temp_thread)
		{
			/*Allocation for position which will be add new node*/
			temp_thread->p_next_threads = T_OS_BLOCK_ALLOC(TASK_ID_TRANSPORT,sizeof(threads_transport_t));
			temp_new_thread = temp_thread->p_next_threads;

			if(SYSLOG_NULL != temp_new_thread)
			{
				temp_new_thread->p_prev_threads = temp_thread;
				temp_new_thread->p_next_threads = SYSLOG_NULL;
				temp_new_thread->state_thread = ITEM_STATE_INACTIVE;
			}
		}
		else
		{
			SYSLOG_TRANSPORT_ERROR_LOGGING("Can't get tail thread from Linked-list");
			return SYSLOG_NULL;
		}

	}
	pthread_mutex_unlock(&g_thread_list_mutex);
	return temp_new_thread;
}
/***********************************************************************
 * FUNCTION NAME:
 *  tp_rm_thread_from_ll_func
 *
 * DESCRIPTION:
 *	Remove thread_connection from linked list.
 *
 * INPUT: 
 *	threads_transport_t *p_thread_list
 *	thread_info_t *thread_info
 *
 * OUTPUT:
 *	
 * RETURN:
 *	val_result
 * GLOBAL DATA REFERRED:
 *
 * CALLED ROUTINES:
 *
 * NOTES:
 *
 ************************************************************************/
U8 tp_rm_thread_from_ll_func(threads_transport_t *p_thread_list,thread_info_t *thread_info)
{

	threads_transport_t *temp_thread;
	U8 val_return = SYSLOG_FALSE;
	

	/*Validate value of thread_info*/
	if(SYSLOG_NOK==thread_info){
		return val_return;
	}

	/*Check that "is there thread_info into linked_list ? "*/

	temp_thread = tp_find_items_into_ll_func(p_thread_list,thread_info);
	if(SYSLOG_NULL == temp_thread)
	{
		/*Can't Find Items into Linked-List*/
		SYSLOG_TRANSPORT_ERROR_LOGGING("Can't find items into linked-list with key_id[%d]",thread_info->thread_key);
		return val_return;
	}
	else
	{
		/*Use mutex to lock g_thread_list*/
		pthread_mutex_lock(&g_thread_list_mutex);

		/*Have three case when delete Node from linked-list
		 * Case 1: Linked-list have only one node ( it is header and tail)
		 * Case 2: Linked-list have more than one node
		 * 	Small Case 1: Node which is delete, is a header node
		 * 	Small Case 2: Node which is delete, is a tail node
		 * 	Small Case 3: Node which is delete, is a middle node
		 * */

		/*Case 1*/
		if((SYSLOG_NULL == temp_thread->p_next_threads) && (SYSLOG_NULL == temp_thread->p_prev_threads))
		{
			T_OS_BLOCK_DEALLOC(TASK_ID_TRANSPORT,temp_thread);
			val_return = SYSLOG_TRUE;
		}
		else
		{
			/*case 2*/
			/*Small Case 1 of case 2*/
			if(SYSLOG_NULL == temp_thread->p_prev_threads)
			{
				p_thread_list = temp_thread->p_next_threads;
				p_thread_list->p_prev_threads = temp_thread->p_prev_threads;
				T_OS_BLOCK_DEALLOC(TASK_ID_TRANSPORT,temp_thread);
				val_return = SYSLOG_TRUE;
			}
			else
			{
				/*Small Case 2 of case 2*/
				if(SYSLOG_NULL == temp_thread->p_next_threads)
				{
					temp_thread->p_prev_threads->p_next_threads = temp_thread->p_next_threads;
					T_OS_BLOCK_DEALLOC(TASK_ID_TRANSPORT,temp_thread);
					val_return = SYSLOG_TRUE;
				}
				else
				{
					/*Small Case 3 of case 2*/
					temp_thread->p_prev_threads->p_next_threads = temp_thread->p_next_threads;
					temp_thread->p_next_threads->p_prev_threads = temp_thread->p_prev_threads;
					T_OS_BLOCK_DEALLOC(TASK_ID_TRANSPORT,temp_thread);
					val_return = SYSLOG_TRUE;
				}
			}
		}
		pthread_mutex_unlock(&g_thread_list_mutex);
	}
	return val_return;
}
/***********************************************************************
 * FUNCTION NAME:
 *  tp_find_items_into_ll_func
 *
 * DESCRIPTION:
 *	Find Items with thread_key into linked_list.
 *
 * INPUT:
 *	threads_transport_t *p_thread_list
 *	thread_info_t *thread_info
 *
 * OUTPUT:
 *
 * RETURN:
 *	val_result
 * GLOBAL DATA REFERRED:
 *
 * CALLED ROUTINES:
 *
 * NOTES:
 *
 ************************************************************************/
void* tp_find_items_into_ll_func(threads_transport_t *p_thread_list,thread_info_t *thread_item)
{
	threads_transport_t *tmp_thread = SYSLOG_NULL;

	/*Validate input parameter*/
	if((SYSLOG_NULL == p_thread_list) || (SYSLOG_NULL == thread_item))
	{
		return SYSLOG_NULL;
	}

	tmp_thread = p_thread_list;
	while(SYSLOG_NULL != tmp_thread)
	{
		if(tmp_thread->data_thread.thread_key == thread_item->thread_key)
		{
			return tmp_thread;
		}
		tmp_thread = tmp_thread->p_next_threads;
	}
	return SYSLOG_NULL;
}
/***********************************************************************
 * FUNCTION NAME:
 *  tp_rm_empty_item_from_ll_func
 *
 * DESCRIPTION:
 *	Find empty Items with pointer into linked_list and remove it.
 *
 * INPUT:
 *	threads_transport_t *p_thread_list
 *	thread_info_t *p_removed_thread
 *
 * OUTPUT:
 *
 * RETURN:
 *	val_result
 * GLOBAL DATA REFERRED:
 *
 * CALLED ROUTINES:
 *
 * NOTES:
 *
 ************************************************************************/
U8 tp_rm_empty_item_from_ll_func(threads_transport_t *p_thread_list,threads_transport_t *p_removed_thread)
{
	threads_transport_t *temp_thread;
	U8 val_result = SYSLOG_FALSE;


	/*Validate value of thread_info*/
	if(SYSLOG_NULL == p_thread_list)
	{
		return val_result;
	}

	/*Use mutex to lock g_thread_list*/
	pthread_mutex_lock(&g_thread_list_mutex);

	/*Have three case when delete Node from linked-list
	* Case 1: Linked-list have only one node ( it is header and tail)
	* Case 2: Linked-list have more than one node
	* 	Small Case 1: Node which is delete, is a header node
	* 	Small Case 2: Node which is delete, is a tail node
	* 	Small Case 3: Node which is delete, is a middle node
	* */

	/*Case 1*/
	if((SYSLOG_NULL == p_removed_thread->p_next_threads) && (SYSLOG_NULL == p_removed_thread->p_prev_threads))
	{
		T_OS_BLOCK_DEALLOC(TASK_ID_TRANSPORT,p_removed_thread);
		p_thread_list = SYSLOG_NULL;
		val_result = SYSLOG_TRUE;
	}
	else
	{
		/*case 2*/
		/*Small Case 1 of case 2*/
		if(SYSLOG_NULL == p_removed_thread->p_prev_threads)
		{
			p_thread_list = p_removed_thread->p_next_threads;
			p_thread_list->p_prev_threads = p_removed_thread->p_prev_threads;
			T_OS_BLOCK_DEALLOC(TASK_ID_TRANSPORT,p_removed_thread);
			val_result = SYSLOG_TRUE;
		}
		else
		{
			/*Small Case 2 of case 2*/
			if(SYSLOG_NULL == p_removed_thread->p_next_threads)
			{
				p_removed_thread->p_prev_threads->p_next_threads = p_removed_thread->p_next_threads;
				T_OS_BLOCK_DEALLOC(TASK_ID_TRANSPORT,p_removed_thread);
				val_result = SYSLOG_TRUE;
			}
			else
			{
				/*Small Case 3 of case 2*/
				p_removed_thread->p_prev_threads->p_next_threads = p_removed_thread->p_next_threads;
				p_removed_thread->p_next_threads->p_prev_threads = p_removed_thread->p_prev_threads;
				T_OS_BLOCK_DEALLOC(TASK_ID_TRANSPORT,p_removed_thread);
				val_result = SYSLOG_TRUE;
			}
		}
	}
	pthread_mutex_unlock(&g_thread_list_mutex);
	return val_result;
}
/***********************************************************************
 * FUNCTION NAME:
 *  tp_rm_empty_item_from_ll_func
 *
 * DESCRIPTION:
 *	Find empty Items with pointer into linked_list and remove it.
 *
 * INPUT:
 *	threads_transport_t *p_thread_list
 *	thread_info_t *p_removed_thread
 *
 * OUTPUT:
 *
 * RETURN:
 *	val_result
 * GLOBAL DATA REFERRED:
 *
 * CALLED ROUTINES:
 *
 * NOTES:
 *
 ************************************************************************/
U8 tp_check_connection_from_ll_func(threads_transport_t *p_thread_list, \
		struct sockaddr_in dst_addr)
{
	U8 val_result = SYSLOG_FAILURE;
	threads_transport_t *temp_thread = SYSLOG_NULL;
	temp_thread = p_thread_list;
	struct sockaddr_in temp_addr;
	socklen_t lenght;

	U8 dst_port;
	U8 temp_port;
	U8 *p_dst_addr = SYSLOG_NULL;
	U8 *p_temp_addr = SYSLOG_NULL;
	dst_port = ntohs(dst_addr.sin_port);
	p_dst_addr = inet_ntoa(dst_addr.sin_addr);
	//SYSLOG_TRANSPORT_DEBUG_LOGGING("dest addr : %s",p_dst_addr);
	while(SYSLOG_NULL != temp_thread)
	{

		/*Check all connection thread*/
		if(CONNECTION_THREAD_TYPE == temp_thread->data_thread.thread_type
				&& SYSLOG_FAILURE != temp_thread->data_thread.socket_id )
		{

			if(SYSLOG_FAILURE != getsockname(temp_thread->data_thread.socket_id,(struct sockaddr*)&temp_addr,&lenght))
			{

				temp_port = ntohs(temp_addr.sin_port);
				p_temp_addr = inet_ntoa(temp_addr.sin_addr);
				//SYSLOG_TRANSPORT_DEBUG_LOGGING("%s",p_temp_addr);
				if((temp_port  == dst_port) && (strcmp(p_dst_addr,p_temp_addr) == 0))
				{
					return temp_thread->data_thread.socket_id;
				}
			}
			else
			{

				SYSLOG_TRANSPORT_ERROR_LOGGING("getsockname is failure.");
			}
		}
		temp_thread = temp_thread->p_next_threads;
	}
	return val_result;
}
